//============================================================================
// Name        : fg.cc
// Authors     : Luca Fabbrini, Giulio Giovannetti
// Version     : 1.0
// Date        : 10/02/2009
// Copyright   : Copyright (C) 2009 Universita' degli Studi di Firenze
// Licence     : This program is free software; you can redistribute it and/or
//               modify it under the terms of the GNU General Public License
//               as published by the Free Software Foundation; either version 2
//               of the License, or (at your option) any later version.
//
//               This program is distributed in the hope that it will be useful,
//               but WITHOUT ANY WARRANTY; without even the implied warranty of
//               MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//               GNU General Public License for more details.
//
//               You should have received a copy of the GNU General Public License
//               along with this program; if not, write to the Free Software
//               Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// Description : FG MANET routing module.
//============================================================================

#include "IPControlInfo.h"
#include "IPAddress.h"
#include "ControlManetRouting.h"
#include "AirFrame_m.h"
#include "XLayer.h"
#include "fg.h"



Define_Module(FG);



//to support WATCH macros
std::ostream& operator<<(std::ostream& os, const FG::IPVect& ipv){
	for(FG::IPVect::const_iterator it= ipv.begin(); it!=ipv.end(); it++)
        os << *it << " ";
    return os;
}

std::ostream& operator<<(std::ostream& os, const FG::MapIPVect& mapipv){
	for(FG::MapIPVect::const_iterator it= mapipv.begin(); it!=mapipv.end(); it++)
        os << it->first << "----------> "<< it->second;
    return os;
}

std::ostream& operator<<(std::ostream& os, const FG::MapOfInt& mapint){
	for(FG::MapOfInt::const_iterator it= mapint.begin(); it!=mapint.end(); it++)
        os << it->first << "----------> "<< it->second;
    return os;
}

std::ostream& operator<<(std::ostream& os, const FG::MapOfIP & mapip){
	for(FG::MapOfIP::const_iterator it= mapip.begin(); it!=mapip.end(); it++)
        os << it->first << "----------> "<< it->second;
    return os;
}



FG::FG(){

	is_init = false;
	helloEvent=NULL;
	tableUpdate=NULL;
	updateStat=NULL;

}

FG::~FG(){

	cancelAllEventInMapOfEvent();
	cancelAllTimer();
	cancelAndDelete(helloEvent);
	cancelAndDelete(tableUpdate);
	cancelAndDelete(updateStat);
}



void FG::initialize(int stage) {

	is_init=true;
	helloPeriod=par("helloPeriod");
	newPDDelay=par("newPDDelay");
	newTestDelay=par("newTestDelay");
	resetPeriod=par("resetPeriod");
	perThreshold=par("perThreshold");
	perPenalty=par("perPenalty");
	updateStatPeriod=par("updateStatPeriod");

	bitrate = 0;
	packetSize = 0;
	timeInQueue = 0;
	numDrop = 0;

	numRREQsent = 0;
	numRREPsent = 0;
	numRTESTsent = 0;
	numRTESTAcksent = 0;
	numRERRsent = 0;
	numHELLOsent = 0;
	numControlSent = 0;
	numControlSentvar = 0;

	numRREQrec = 0;
	numRREPrec = 0;
	numRTESTrec = 0;
	numRTESTAckrec = 0;
	numRERRrec = 0;
	numHELLOrec = 0;
	numControlRec = 0;
	numControlRecvar = 0;

	numPD = 0;
	numTEST = 0;
	numErrorInPD = 0;

	WATCH(packetSize);
	WATCH(bitrate);


	WATCH(timeInQueue);
	WATCH_MAP(waitingTime);

	WATCH(numDrop);
	WATCH(myAddr);

	WATCH_MAP(isPD);
	WATCH_MAP(howManyNewPD);
	WATCH_MAP(consecutivePD);
	WATCH_MAP(rreqForwardedMap);

	WATCH_MAP(isUpdated);
	WATCH_MAP(tableOfNeighborPER);

	WATCH_MAP(previusHop);
	WATCH_MAP(nominalTXtime);

	//WATCH(if_id);
	//WATCH_PTRMAP(mapOfEvent);
	//WATCH_MAP(tableOfNeighborAssociability);
	//WATCH_MAP(numOfHello);
	//WATCH_MAP(rreqRecievedMap);

	controlPacketSent.setName("FG Control Packet Sent");
	controlPacketRec.setName("FG Control Packet Rec");

	if (stage==4) {
		//set interfaceVector and all the other structures of ManetRoutingBase
		registerRoutingModule();
		linkLayerFeeback();
		inet_irt=RoutingTableAccess().get();
		inet_nb=NotificationBoardAccess().get();
		inet_nb->subscribe(this,NF_RADIOSTATE_FG);
		inet_nb->subscribe(this,NF_MACQUEUE_FG);
		ie=getWlanInterfaceEntry(0);
		if_id=ie->getInterfaceId();
		myAddr=ie->ipv4Data()->getIPAddress();
		helloEvent = new cMessage("helloEvent");
		scheduleAt(simTime(), helloEvent);
		tableUpdate = new cMessage("tableUpdate");
		updateStat = new cMessage("updateStat");
		scheduleAt(simTime(), updateStat);
		counter = 1;
		newCounter = 0;

	}
}



void FG::handleMessage(cMessage *msg) {
	IPDatagram* ipDgram=NULL;

	if (is_init==false){
		opp_error("FG has not been initialized");
		return;
	}

	if( dynamic_cast<ControlManetRouting *>(msg) ){
		ControlManetRouting * control= check_and_cast<ControlManetRouting *>(msg);
		//control if is a MANET_ROUTE_NOROUTE
		if(control->getOptionCode() == MANET_ROUTE_NOROUTE){
			ipDgram=check_and_cast<IPDatagram *>( control->decapsulate() );
			cPolymorphic* ctrl= ipDgram->removeControlInfo();
			if(ctrl)
				delete ctrl;
			EV << "FG rec datagram  " << ipDgram->getName() << " with dest="
			   << ipDgram->getDestAddress().str() << endl;

			processPacketNoRoute(ipDgram);

		}
		//control if is a MANET_ROUTE_UPDATE
		if(control->getOptionCode() == MANET_ROUTE_UPDATE){
			ipDgram=check_and_cast<IPDatagram *>( control->decapsulate() );
			cPolymorphic* ctrl= ipDgram->removeControlInfo();
			if(ctrl)
				delete ctrl;

			processPacketUpdate(ipDgram);

		}
		delete msg;
		return;
	}
	//control if is a FGMessage (RREQ,RERR,RREP,RTEST,RTESTAck,HELLO)
	else if(dynamic_cast<FGMessage *>(msg)){
		FGMessage* fgmsg=check_and_cast<FGMessage *>(msg);
		processFGMessage(fgmsg);
	}

	else if (msg->isSelfMessage()) {
		if(msg==helloEvent){
			if (newCounter < 5)
				scheduleAt(simTime() + helloPeriod / (5-newCounter), helloEvent);
			else
				scheduleAt(simTime() + helloPeriod, helloEvent);

			if (newCounter == 5)
				scheduleAt(simTime() + helloPeriod / 2, tableUpdate);

			newCounter++;
			sendHello();
		}else if(msg==tableUpdate){
			scheduleAt(simTime() + helloPeriod, tableUpdate);
			handleTableUpdate();
		}else if(msg==updateStat){
			scheduleAt(simTime() + updateStatPeriod, updateStat);
			updateGlobalStatistics();
		}else if(searchForDestination(msg)!=IPAddress::UNSPECIFIED_ADDRESS){
			IPAddress dest=searchForDestination(msg);
			if (isPD[dest] == false)
				opp_error("Error: PD is not active but a new PD is required.");

			if (consecutivePD[dest] >= 3) {//# prima era howManyPD[dest]
				if(dynamic_cast<IPDatagram*>(mapfgqueue[dest].front())){
					IPDatagram *pkt=check_and_cast<IPDatagram*>(mapfgqueue[dest].front());
					icmpAccess.get()->sendErrorMessage(pkt->dup(), ICMP_DESTINATION_UNREACHABLE, ICMP_DU_HOST_UNREACHABLE);
					numErrorInPD++;
					isPD[dest]=false;
					consecutivePD[dest]=0;//#
					int numPacket = mapfgqueue[dest].length();//#
				    numDrop += numPacket;//#
				    mapfgqueue[dest].clear();//#
					// x ora, visto che icmp non fa altro che segnalare che c'e' stato un errore,
					// invece di bloccare il flusso, forzo una nuova pathDiscovery quando arriva
					// un nuovo pkt.
					delete msg;
					return;
				}else{
					opp_error("Error: requested path discovery without packet to send");
					delete msg;
					return;
				}
			}else{
				pathDiscovery(dest);
				delete msg;
				return;
			}
		}else{
			IPAddress src,dest;
			if(searchForCommodity(msg,src,dest)){
				if(strcmp(msg->getName(),"newTest")==0){
					getAndRemoveTimerForCommodity(src,dest,"newTest");
					inet_irt->setIsCommoditySuspended(src,dest,false);//#
					processFailedTest(src,dest);
					delete msg;
					return;
				}//else if new timer
			}
		}
	}
	//if it is anything else
	else {
		delete msg;
		return;
	}
}



void FG::processPacketUpdate(IPDatagram* datagram){

	IPAddress src= datagram->getSrcAddress();
	IPAddress dest= datagram->getDestAddress();
	IPSourceRoutingOption ipsro= datagram->getSourceRoutingOption();
	IPAddress previus =ipsro.getRecordAddress(0);

	if(src!=IPAddress::UNSPECIFIED_ADDRESS && previus!=IPAddress::UNSPECIFIED_ADDRESS && datagram->getTransportProtocol()!= IP_PROT_MANET )
		previusHop[src][dest]=previus;



	if(dynamic_cast<FGMessage*>(datagram->getEncapsulatedMsg())){
		delete datagram;
		return;
	}

	if(dest==myAddr){
		IPRecordRouteOption& iprro=datagram->getRecordRoute();
		bool equal=true;
		for(unsigned int j=0; j<MAX_IPADDR_OPTION_ENTRIES; j++){
			if(lastRoute.getRecordAddress(j)!=iprro.getRecordAddress(j)){
				equal=false;
				break;
			}else if(lastRoute.getRecordAddress(j)==iprro.getRecordAddress(j)&&lastRoute.getRecordAddress(j)==IPAddress::UNSPECIFIED_ADDRESS){
				equal=true;
				break;
			}

		}
		if(!equal){
			lastRoute=iprro;
			std::string name;
			std::string route;
			name="Commodity ["+ src.str()+"] - [" + dest.str()+"] : ";
			unsigned int i=1;
			for(; i< MAX_IPADDR_OPTION_ENTRIES; i++){
				if(iprro.getRecordAddress(i)!= IPAddress::UNSPECIFIED_ADDRESS)
					route= route + (iprro.getRecordAddress(i)).str() + " - ";
				else
					break;
			}
			if(i>1)
				recordScalar((name+route).c_str(),simTime());
			else
				recordScalar(name.c_str(),simTime());


		}
	}

	delete datagram;
	return;


}



void FG::processPacketNoRoute(IPDatagram* datagram) {

	IPAddress src = datagram->getSrcAddress();
	IPAddress dest = datagram->getDestAddress();

	if (src==IPAddress::UNSPECIFIED_ADDRESS)
		src = myAddr;

	if(dest == IPAddress::UNSPECIFIED_ADDRESS){
		opp_error("packet with IP Address of destination not specified");
		return;
	}
	else if (dest == IPAddress::ALLONES_ADDRESS) {
		send(datagram,"to_ip");
		return;
	}
	else
		if (inet_irt->getIsCommoditySuspended(src,dest)==false) {
			if(src!=myAddr){
				delete datagram;
				numDrop++;
			}
			mapfgqueue[dest].insert(datagram);
			if (isPD[dest] == true)
				return;
			else pathDiscovery(dest);
		}
		else {
			suspendedQueue[src][dest].insert(datagram);
			return;
		}
}



void FG::pathDiscovery(IPAddress& dest) {

	mapOfEvent[dest]= new cMessage("newPDEvent");
	isPD[dest]=true;
	int number = ++howManyNewPD[dest];
	consecutivePD[dest]++;//#
	int delay=1;//#
	for(int i=1; i< consecutivePD[dest]; i++){//#
		delay *=2;//#
	}
	scheduleAt(simTime() + (newPDDelay*delay), mapOfEvent[dest]);//#


	//scheduleAt(simTime() + (newPDDelay*number), mapOfEvent[dest]);
	FGMessage* rreq = new FGMessage("RREQ");
	rreq->setSrcAddr(myAddr);
	rreq->setDestAddr(dest);
	rreq->setSender(myAddr);
	rreq->setType(FG_RREQ);
	int l = countBitLength(rreq);
	rreq->setBitLength(l);
	rreq->setSeqNum(howManyNewPD[dest]);
	EV << "Path Discovery number " << howManyNewPD[dest] << ", started from: "
	   << myAddr.str() << " for destination: " << dest.str() <<"\n";
	forwardRREQ(rreq);

}



void FG::forwardRREQ(FGMessage* rreq){

	IPControlInfo *ctrl = new IPControlInfo();
	ctrl->setDestAddr(IPAddress::ALLONES_ADDRESS);
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	delete rreq->removeControlInfo();
	rreq->setControlInfo(ctrl);
	numRREQsent++;
	numControlSent++;
	sendDelayed(rreq,uniform(0,0.003),"to_ip");

}



void FG::processFGMessage(FGMessage* m){

	int type=m->getType();

	switch (type) {

		case FG_RREQ:
			processRREQ(m);
			numRREQrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			break;

		case FG_RREP:
			numRREPrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			processRREP(m);
			break;

		case FG_RERR:
			numRERRrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			processRERR(m);
			break;
		case FG_HELLO:
			numHELLOrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			processHello(m);
			break;
		case FG_RTEST:
			numRTESTrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			processRTEST(m);
			break;
		case FG_RTEST_ACK:
			numRTESTAckrec++;
			numControlRec++;
			//controlPacketRec2.record(1);
			processRTESTAck(m);
			break;

		default:
			opp_error("Unable to process FGMessage");
			break;
	}
	return;
}



void FG::processRREP(FGMessage* m) {

	IPAddress src=m->getSrcAddr();
	IPAddress dest=m->getDestAddr();
	int seqNum = m->getSeqNum();//#

	if (rreqForwardedMap[src][dest] > seqNum)//# //ok, but not send backward rrep of old path discovery that could be important for the quick start of the flow transmission
		return;//#

	rreqForwardedMap[src][dest]=seqNum;//#




	if (myAddr==src) {
		updateRoutingTable(m);
		sendAllPacket(dest);
		if (isPD[dest]==true) {
			 cancelAndDelete(mapOfEvent[dest]);
			 mapOfEvent.erase(dest);
			 isPD[dest] = false;//#
			 consecutivePD[dest] = 0;//#
		 }

		delete m;
		return;
	}
	else {
		bool isBetter = updateRoutingTable(m);
		EV << "processRREP: " << isBetter << endl;
		if(isBetter)
			preventingLoopSend(m);
		else {
			delete m;
			return;
		}
	}
}



void FG::processRREQ(FGMessage* m) {

	IPAddress src = m->getSrcAddr();
	IPAddress dest = m->getDestAddr();
	IPAddress sender = m->getSender();
	int seqNum = m->getSeqNum();

	if (myAddr==dest)
		createAndSendRREP(m);
	else
		if (myAddr!=src) {
			if (rreqForwardedMap[src][dest] < seqNum) {
				m->setSender(myAddr);
				int l = countBitLength(m);
				m->setBitLength(l);
				forwardRREQ(m);
				rreqForwardedMap[src][dest]=seqNum;
			} else{
				delete m;
				return;
			}
		} else{
			delete m;
			return;
		}
}



void FG::createAndSendRREP(FGMessage* rreq) {

	FGMessage* rrep;
	rrep=new FGMessage("RREP");
	rrep->setSrcAddr(rreq->getSrcAddr());
	rrep->setDestAddr(rreq->getDestAddr());
	rrep->setSeqNum(rreq->getSeqNum());//#
	rrep->setType(FG_RREP);
	rrep->insertMyAddr(myAddr);
	rrep->setSender(myAddr);
	rrep->incNumHop();
	int l = countBitLength(rrep);
	rrep->setBitLength(l);
	forwardRREP(rrep,rreq->getSender());
	delete rreq;

}



void FG::forwardRREP(FGMessage* rrep, const IPAddress& nextHop){

	IPControlInfo* ctrl = new IPControlInfo();
	ctrl->setDestAddr(nextHop);
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	rrep->setControlInfo(ctrl);
	numRREPsent++;
	numControlSent++;
	send(rrep,"to_ip");
	//sendDelayed(rrep,uniform(0,0.005),"to_ip");
}



void FG::sendAllPacket(IPAddress& dest) {

	IPDatagram* datagram;
	while (!mapfgqueue[dest].isEmpty()) {
		datagram = check_and_cast <IPDatagram*>(mapfgqueue[dest].pop());
		send(datagram, "to_ip");
	}

}



bool FG::updateRoutingTable(FGMessage* rrep) {

	FGIPRoute* fgiproute;
	fgiproute = createRouteEntry(rrep);
	inet_irt->addFGRoute(fgiproute);
	return inet_irt->isBestRoute(fgiproute);

}



void FG::preventingLoopSend(FGMessage* m) {

	bool loop = false;
	FGMessage *rrep;
	IPAddress src = m->getSrcAddr();
	IPAddress dst = m->getDestAddr();
	int n = m->getRouteArraySize();
	TableOfNeighborAssociability::iterator it;

	for (it=tableOfNeighborPER.begin(); it != tableOfNeighborPER.end(); it++) {
		if((it->second)<perThreshold){
			for (int i = 0; i < n; i++)
				if ( (it->first) == m->getRoute(i))
					loop = true;
			if (loop == false) {
				rrep = new FGMessage(*m);
				rrep->setType(FG_RREP);
				rrep->setSrcAddr(src);
				rrep->setDestAddr(dst);
				rrep->setSender(myAddr);
				updateRREPMetric(rrep,m,it->first);
				rrep->insertMyAddr(myAddr);
				rrep->incNumHop();
				int l = countBitLength(rrep);
				rrep->setBitLength(l);
				forwardRREP(rrep,it->first);
			} else
				loop = false;

		}

	}
	delete m;

}



FGIPRoute* FG::createRouteEntry(FGMessage *m) {

	FGIPRoute *entry=NULL;

	entry = new FGIPRoute();

	IPAddress gateway = m->getSender();
	IPAddress dst     = m->getDestAddr();
	int seqNum = m->getSeqNum();//#

	IPAddress netmask = dst.getNetworkMask().getInt();

	///Sequence Number
	entry->setSeqNum(seqNum);//#
	/// Destination
	entry->setHost(dst);
	/// Route mask
	entry->setNetmask(netmask);
	/// Next hop
	entry->setGateway(gateway);
	/// Metric ("cost" to reach the destination)
	entry->setMetric(m->getNumHop());
	/// Interface name and pointer
	entry->setInterface(ie);
	//entry->setInterface(getInterfaceWlanByAddress(iface));
	/// Route type: Direct or Remote
	if(entry->getGateway().isUnspecified())
		entry->setType (IPRoute::DIRECT);
	else
		entry->setType (IPRoute::REMOTE);
	/// Source of route, MANUAL by reading a file,
	/// routing protocol name otherwise
	entry->setSource(IPRoute::MANET);

	/// Routing MetricT
	double txTime = nominalTXtime[gateway]/(1-tableOfNeighborPER[gateway]) + timeInQueue;
	entry->setMetricT(m->getMetricT()+txTime);

	/// Sender (which has sent the first RREQ)
	entry->setSourceAddress(m->getSrcAddr());
	return entry;

}



const IPAddress& FG::searchForDestination(cMessage* m){

	MapOfEvent::iterator it;

	for(it = mapOfEvent.begin(); it != mapOfEvent.end(); it++)
		if(it->second == m)
			return it->first;

	return IPAddress::UNSPECIFIED_ADDRESS;

}



int FG::countBitLength(FGMessage* m) {

	int type=m->getType();

	switch(type){

		case FG_RREQ:
			return 	8+		//generic header
			        32+		//source Address
			        32+		//destination Address
			        3+		//type
			        8+		//numHop (ttl_max=255)
			        8+		//sequence Number
			        64;		//metricT floating point with double precision IEEE 754
			break;

		case FG_RREP:
			return 	8+		//generic header
			        32+		//source Address
			        32+		//destination Address
			        3+		//type
			        8+		//numHop (ttl_max=255)
			        8+		//sequence Number
			        64+		//metricT floating point with double precision IEEE 754
			        (m->getRouteArraySize())*32;	//list of ipv4 address of the nodes
			break;

		case FG_HELLO:
			return 	8+		//generic header
			        3;		//type
			break;

		case FG_RERR:
			return 	8+		//generic header
			        32+		//source Address
			        32+		//destination Address
			        3;		//type
			break;

		case FG_RTEST:
			return 	8+		//generic header
			        32+		//source Address
			        32+		//destination Address
			        32+		//Sender of the RTEST
			        3+		//type
			        8+		//sequence number
			        (m->getRouteArraySize())*32;	//list of ipv4 address of the nodes
			break;

		case FG_RTEST_ACK:
			return 	8+		//generic header
			        32+		//source Address
			        32+		//destination Address
			        32+		//Sender of the RTEST
			        3+		//type
			        8+		//sequence number
			        (m->getRouteArraySize())*32;	//list of ipv4 address of the nodes
			break;

		default:
			opp_error("packet type not allowed in countBitLength");
			break;
	}
	return -1; // to avoid warning, not really necessary
}



void FG::cancelAllEventInMapOfEvent() {

	MapOfEvent::iterator it;

	for(it=mapOfEvent.begin(); it != mapOfEvent.end(); it++)
		cancelAndDelete(it->second);

}



void FG::processHello(FGMessage *m) {

	IPAddress sender = m->getSender();

	double associability=(double)(++(numOfHello[sender])) * helloPeriod /(simTime());

	if (associability > 1)
		tableOfNeighborAssociability[sender] = 1;
	else
		tableOfNeighborAssociability[sender]=associability;

	if (associability < 0)
		tableOfNeighborAssociability[sender] = 0;
	else
		tableOfNeighborAssociability[sender] = associability;

	delete m;

}



void FG::sendHello() {

	FGMessage *hello = new FGMessage("HELLO");
	hello->setType(FG_HELLO);
	hello->setSender(myAddr);
	int l = countBitLength(hello);
	hello->setBitLength(l);
	forwardHello(hello);

}



void FG::forwardHello(FGMessage* hello) {

	IPControlInfo* ctrl= new IPControlInfo();
	ctrl->setDestAddr(IPAddress::ALLONES_ADDRESS);
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	delete hello->removeControlInfo();
	hello->setControlInfo(ctrl);
	numHELLOsent++;
	numControlSent++;
	//controlPacketSent2.record(1);
	sendDelayed(hello,uniform(0,0.01),"to_ip");

}



int FG::countNumOfAssociatedNeighbor() {
	int n = 0;
	TableOfNeighborAssociability::iterator it;
	for (it = tableOfNeighborPER.begin(); it != tableOfNeighborPER.end(); it++)
		if ((it->second) < perThreshold)
			n++;
	return n;

}



void FG::updateRREPMetric(FGMessage *new_rrep, FGMessage *old_rrep, const IPAddress& destRREP) {

	double metricT = old_rrep->getMetricT();
	IPAddress srcRREP = old_rrep->getSender();

	//update metric T
	metricT += nominalTXtime[srcRREP]/(1-tableOfNeighborPER[srcRREP])+ timeInQueue;
	new_rrep->setMetricT(metricT);

}



void FG::processLinkBreak(const cPolymorphic *details) {

	IPDatagram *dgram;
	cPacket *packet;

	if (dynamic_cast<IPDatagram*>(const_cast<cPolymorphic*>(details))) {
		dgram = check_and_cast<IPDatagram*>(details);
		IPAddress dest = dgram->getDestAddress();
		IPAddress src = dgram->getSrcAddress();

		// control if it is a FGMessage
		packet = dgram->decapsulate(); //FIXME: si dovrebbe usare getEncapsulate...
		if (dynamic_cast<FGMessage*>(packet)){
			dgram->encapsulate(packet);
			return;
		}
		dgram->encapsulate(packet);

		suspendedQueue[src][dest].insert(dgram->dup());

		if(inet_irt->getIsCommoditySuspended(src,dest) == true){
			EV << "IsCommoditySuspended is: TRUE" << endl;
			return;
		}

		if (dest==IPAddress::ALLONES_ADDRESS)
			opp_error("procesLinkBreak error: unable to process broadcast packets");

		processFailedTest(src,dest);
		return;
	}
	return;

}



void FG::createAndSendRTEST(IPAddress& src, IPAddress& dest, int seqNum) {

	FGMessage *rtest;
	rtest = new FGMessage("RTEST");
	rtest->setSrcAddr(src);
	rtest->setDestAddr(dest);
	rtest->setType(FG_RTEST);
	rtest->insertMyAddr(myAddr);
	rtest->setSender(myAddr);
	rtest->setSeqNum(seqNum);
	int l = countBitLength(rtest);
	rtest->setBitLength(l);
	forwardRTEST(rtest);

}



void FG::forwardRTEST(FGMessage* rtest) {

	IPAddress src  = rtest->getSrcAddr();
	IPAddress dest = rtest->getDestAddr();

	IPRoute* iproute;
	FGIPRoute* route;

	if ( inet_irt->getIsCommoditySuspended(src,dest)==true ){
		//if findFGBestMatchingRoute is locked
		inet_irt->setIsCommoditySuspended(src,dest,false);
		//unlock findFGBestMatchingRoute
		iproute=const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
		inet_irt->setIsCommoditySuspended(src,dest,true);
		//re-lock findFGBestMatchingRoute
		if(dynamic_cast<FGIPRoute*>(iproute)){
			route = check_and_cast<FGIPRoute*>(iproute);
			EV<<"route for RTEST: "<<route->info()<<endl;
		}else{
			opp_error("Error (3)");
			return;
		}
	}
	else {//intermediate node
		iproute=const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
		if(dynamic_cast<FGIPRoute*>(iproute)){
			route = check_and_cast<FGIPRoute*>(iproute);
			EV<<"route for RTEST: "<<route->info()<<endl;
		}else{
			delete rtest;
			return;
		}


	}

	IPControlInfo* ctrl = new IPControlInfo();
	ctrl->setDestAddr(route->getGateway());
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	delete rtest->removeControlInfo();
	rtest->setControlInfo(ctrl);
	numRTESTsent++;
	numControlSent++;
	//controlPacketSent2.record(1);
	send(rtest,"to_ip");

}



void FG::forwardRTESTAck(FGMessage* rtestAck) {

	IPControlInfo* ctrl = new IPControlInfo();
	ctrl->setDestAddr(rtestAck->getAndDeleteLastAddress());
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	delete rtestAck->removeControlInfo();
	int l = countBitLength(rtestAck);
	rtestAck->setBitLength(l);
	rtestAck->setControlInfo(ctrl);
	numRTESTAcksent++;
	numControlSent++;
	//controlPacketSent2.record(1);
	send(rtestAck,"to_ip");

}



void FG::processRTESTAck(FGMessage* m) {

	IPAddress  src = m->getSrcAddr();
	IPAddress  dest = m->getDestAddr();

	if (myAddr==m->getSender()) {
		if (m->getSeqNum()==howmanytest[src][dest]){
			inet_irt->setIsCommoditySuspended(src,dest,false);
			sendAllSuspendedPackets(src,dest);
			cancelAndDelete(getAndRemoveTimerForCommodity(src,dest,"newTest"));
			delete m;
			return;
		} else {
			delete m;
			return;
		}
	} else
		forwardRTESTAck(m);

}



void FG::processRTEST(FGMessage *rtest) {

	IPAddress src = rtest->getSrcAddr();
	IPAddress dest = rtest->getDestAddr();
	IPAddress sender = rtest->getSender();

	if (myAddr==dest)
		createAndSendRTESTAck(rtest);
	else{//#
		if (myAddr != rtest->getSender()) {
			bool loop=false;//#
			int size=rtest->getRouteArraySize();//#
			for(int i = 0; i < size; i++){//#
				if(myAddr== rtest->getRoute(i)){//#
					loop=true;//#
					break;//#
				}
			}
			if(loop){//#
				delete rtest;//#
				return;//#

			}
			rtest->insertMyAddr(myAddr);
			forwardRTEST(rtest);
		}
		else {
			//a RTEST could come back to sender because loops are not avoided on backup routes
			delete rtest;
			return;
		}

	}
}



void FG::createAndSendRTESTAck(FGMessage *rtest) {

	FGMessage *rtestAck;
	rtestAck = new FGMessage(*rtest);
	rtestAck->setType(FG_RTEST_ACK);
	rtestAck->setName("RTEST_ACK");
	forwardRTESTAck(rtestAck);
	delete rtest;

}



void FG::sendAllSuspendedPackets(IPAddress& src,IPAddress& dest) {

	IPDatagram* datagram;
	while (!suspendedQueue[src][dest].isEmpty()) {
		datagram = check_and_cast <IPDatagram*>(suspendedQueue[src][dest].pop());
		send(datagram, "to_ip");
	}
}



void FG::cancelAllTimer(){
	MapOfQueue::iterator it;
	MapOfCommodity::iterator et;
	cMessage *msg;
	for (et = mapOfCommodity.begin(); et != mapOfCommodity.end(); et++)
		for (it = (et->second).begin(); it != (et->second).end(); it++)
			while((it->second).empty()==false){
				msg=check_and_cast<cMessage*>((it->second).back());
				(it->second).pop_back();
				cancelAndDelete(msg);
			}
}



cMessage* FG::getAndRemoveTimerForCommodity(IPAddress& src, IPAddress& dest, const char* name){

	unsigned int n = mapOfCommodity[src][dest].size();
	cMessage* msg;
	cObject* obj;

	for(unsigned int i=0; i < n; i++){
		if(dynamic_cast<cMessage*>(mapOfCommodity[src][dest].at(i))){
			obj=mapOfCommodity[src][dest].at(i);
			msg=check_and_cast<cMessage*>(obj);
			if(strcmp(msg->getName(),name)==0){
				mapOfCommodity[src][dest].erase(mapOfCommodity[src][dest].begin()+i);
				return msg;
			}
		}else{
			opp_error("error in casting cObject->cMessage in getAndRemoveTimerForCommodity");
			return msg;
		}
	}
	return msg;

}


bool FG::searchForCommodity(cMessage* msg, IPAddress& src, IPAddress& dest) {

	MapOfQueue::iterator it;
	MapOfCommodity::iterator et;

	for (et = mapOfCommodity.begin(); et != mapOfCommodity.end(); et++){
		for (it = (et->second).begin(); it != (et->second).end(); it++){
			unsigned int n=(it->second).size();
			cMessage *temp;
			for(unsigned int i=0; i < n; i++){
				if(dynamic_cast<cMessage*>((it->second).at(i))){
					temp=check_and_cast<cMessage*>((it->second).at(i));
					if(strcmp(msg->getName(),temp->getName())==0){
						src = et->first;
						dest = it->first;
						return true;
					}
				}else{
					opp_error("error in casting cObject->cMessage in searchForCommodity");
					return false;
				}
			}
		}
	}
	return false;

}




void FG::processFailedTest(IPAddress& src, IPAddress& dest) {

	int n = howmanytest[src][dest];
	FGIPRoute *old_route, *new_route;

			/*
			 * packets that was yet in MAC queue when the first packet had failed
			 * can call this function when fails itself!!!
			 * */
	if (inet_irt->getIsCommoditySuspended(src,dest))//#
		return;//#if there is yet a test of routes, return.

	if (n==0){
		EV << "Failure occurred on link between node " << myAddr << " and "
		   << dest << endl;
	}
	else
		EV << "Test on backup route number: " << n << "for commodity: "
		   << src << " to " << dest << " FAILED." << endl;

	inet_irt->setIsCommoditySuspended(src,dest,false);//unlock findFGBestMatchingRoute
	IPRoute* old_iproute = const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
	if(dynamic_cast<FGIPRoute*>(old_iproute))
		old_route = check_and_cast<FGIPRoute*>(old_iproute);
	else {
		/*
		 * packets that was yet in MAC queue when the first packet had failed
		 * can call this function when fails itself!!!
		 * */
		int numPacket = suspendedQueue[src][dest].length();//#
	    numDrop += numPacket;//#
		suspendedQueue[src][dest].clear();//#
		return;
	}

	inet_irt->removeFGRoute(old_route);

	IPRoute* new_iproute=const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
	if(dynamic_cast<FGIPRoute*>(new_iproute))
		new_route = check_and_cast<FGIPRoute*>(new_iproute);
	else {
		if (myAddr!=src) {
			EV << "No more routes. Drop all packets in suspendedQueue and send RERR." << endl;
			int numPacket = suspendedQueue[src][dest].length();
			numDrop += numPacket;
			suspendedQueue[src][dest].clear();
			createAndSendRERR(src, dest);
			inet_irt->setIsCommoditySuspended(src,dest,false);
			//unlock findFGBestMatchingRoute
			resetCommodity(src,dest);
			return;
		}
		else{
			// if I am the source start a new Path Discovery!
			putAllSuspendedPacketInQueueForSending(dest);
			if(!mapfgqueue[dest].isEmpty())
				pathDiscovery(dest);
			return;
		}
	}

	inet_irt->setIsCommoditySuspended(src,dest,true);
	n = ++howmanytest[src][dest];
	cMessage *msg = new cMessage("newTest");
	mapOfCommodity[src][dest].push_back(msg);
	scheduleAt(simTime() + newTestDelay, msg);
	createAndSendRTEST(src,dest,n);
	return;

}



void FG::createAndSendRERR(IPAddress& src, IPAddress& dest) {

	FGMessage* rerr;
	rerr = new FGMessage("RERR");
	rerr->setSrcAddr(src);
	rerr->setDestAddr(dest);
	rerr->setType(FG_RERR);
	rerr->setSender(myAddr);
	int l=countBitLength(rerr);
	rerr->setBitLength(l);
	forwardRERR(rerr);

}



void FG::forwardRERR(FGMessage *rerr) {

	IPControlInfo* ctrl;
	IPAddress nextHop;
	IPAddress src  = rerr->getSrcAddr();
	IPAddress dest = rerr->getDestAddr();
	nextHop = previusHop[src][dest];

	ctrl = new IPControlInfo();
	ctrl->setDestAddr(nextHop);
	ctrl->setSrcAddr(myAddr);
	ctrl->setProtocol(IP_PROT_MANET);
	ctrl->setInterfaceId(if_id);
	ctrl->setTimeToLive(1);
	delete rerr->removeControlInfo();
	rerr->setControlInfo(ctrl);
	numRERRsent++;
	numControlSent++;
	//controlPacketSent2.record(1);
	send(rerr,"to_ip");

}



void FG::processRERR(FGMessage *m) {

	IPAddress src  = m->getSrcAddr();
	IPAddress dest = m->getDestAddr();
	IPAddress sender  = m->getSender();

	FGIPRoute *old_route, *new_route;
	EV << myAddr << "Process RERR for commodity (" << src << " , " << dest
	   << ") received from: " << sender << endl;

	IPRoute* old_iproute = const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
	if(dynamic_cast<FGIPRoute*>(old_iproute))
		old_route = check_and_cast<FGIPRoute*>(old_iproute);
	else {

	/*	MapOfIPCommodity::iterator mapIt;
		MapOfIP::iterator mapEt;
		mapIt=previusHop.find(src);
		mapEt=(mapIt->second).find(dest);

		if(mapEt!=(mapIt->second).end()){
			EV << "RERR just processed, return...";
			return;
		}
		else{//#
			//opp_error("An error (1B) occurred in removing an entry from Routing Table");
			return;
		}*/
		return;//#
	}

	inet_irt->removeFGRoute(old_route);

	IPRoute* new_iproute=const_cast<IPRoute*>(inet_irt->findFGBestMatchingRoute(src,dest));
	if(dynamic_cast<FGIPRoute*>(new_iproute))
		new_route = check_and_cast<FGIPRoute*>(new_iproute);
	else {
		if (myAddr!=src) {
			EV << "No more routes." << endl;

			MapOfIPCommodity::iterator mapIt;//#
			MapOfIP::iterator mapEt;//#
			mapIt=previusHop.find(src);//#
			mapEt=(mapIt->second).find(dest);//#

			if(mapEt!=(mapIt->second).end()){//#
				forwardRERR(m);//#
				resetCommodity(src,dest);//#
			}
			return;
		}
		else{
			putAllSuspendedPacketInQueueForSending(dest);
			if(!mapfgqueue[dest].isEmpty())
				pathDiscovery(dest);
			delete m;
			return;
		}
	}

	inet_irt->setIsCommoditySuspended(src,dest,true);
	int n=++howmanytest[src][dest];
	cMessage *msg = new cMessage("newTest");
	mapOfCommodity[src][dest].push_back(msg);
	scheduleAt(simTime() + newTestDelay, msg);
	createAndSendRTEST(src,dest,n);

	delete m;
	return;
}



void FG::resetCommodity(IPAddress& src,IPAddress& dest){

	MapOfIPCommodity::iterator mapIt;
	MapOfIP::iterator mapEt;

	mapIt=previusHop.find(src);
	mapEt=(mapIt->second).find(dest);
	(mapIt->second).erase(mapEt);

	//TODO: deallocare la mappa howManyTest[src][dest]
	howmanytest[src][dest]=0;//#
	return;

}



void FG::putAllSuspendedPacketInQueueForSending(IPAddress& dest){

	while(!suspendedQueue[myAddr][dest].isEmpty()){
		cPacket* pkt=suspendedQueue[myAddr][dest].pop();
		mapfgqueue[dest].insert(pkt);
	}

	return;
}



void FG::receiveChangeNotification(int category, const cPolymorphic *details)
{
	Enter_Method("Manet llf");
	if (category == NF_LINK_BREAK)
	{
		if (details==NULL)
			return;
		processLinkBreak(details);
	}
	else if (category == NF_LINK_BREAK)
	{
		processPromiscuous(details);
	}
	else if (category == NF_RADIOSTATE_FG)
	{
		processRadioState(details);
	}
	else if (category == NF_MACQUEUE_FG)
	{
		processMacQueue(details);
	}
}



void FG::processRadioState(const  cPolymorphic *details){

	double per;
	AirFrame* airframe;
	IPAddress sender;
	FGXLayerInfo *instance;
	cPacket *packet;
	FGMessage *hello;

	if (dynamic_cast<FGXLayerInfo*>(const_cast<cPolymorphic*>(details))){
		instance = check_and_cast<FGXLayerInfo*>(details);
		per = instance->getPER();
		airframe = &(instance->getAirFrame());
		bitrate = airframe->getBitrate();
		packetSize = (airframe->getEncapsulatedMsg())->getBitLength();
		packet = airframe->getEncapsulatedMsg();
		if (packet) //FIXME: usare un ciclo finchè packet non è "castabile" come FGMessage
			packet = packet->getEncapsulatedMsg();
		if (packet)
			packet = packet->getEncapsulatedMsg();

		if (dynamic_cast<FGMessage*>(packet)){
			hello = check_and_cast<FGMessage*>(packet);
			if (hello->getType() != FG_HELLO)
				return;
			sender = hello->getSender();
			if (bitrate!=0)
				nominalTXtime[sender]=packetSize/bitrate;
			else
				nominalTXtime[sender]=3600;//FIXME: usare una costante MAX_NOMINAL_TX_TIME

			mapofstatistic[sender].collect(per);
			tableOfNeighborPER[sender]=mapofstatistic[sender].getMean();
			isUpdated[sender] = true;
		}
	}
	return;

}



void FG::processMacQueue(const cPolymorphic* details){

	FGXLayerInfo *instance;

	if (dynamic_cast<FGXLayerInfo*>(const_cast<cPolymorphic*>(details))){
		instance = check_and_cast<FGXLayerInfo*>(details);
		timeInQueue = instance->getTimeMacQueue();
		TableOfNeighborAssociability::iterator itPer;
		for (itPer=tableOfNeighborPER.begin(); itPer != tableOfNeighborPER.end(); itPer++) {

			waitingTime[itPer->first]=(timeInQueue + nominalTXtime[itPer->first]/(1-tableOfNeighborPER[itPer->first]));
		}


	}

	return;

}


void FG::handleTableUpdate(){

	MapBool::iterator it;

	if (counter % resetPeriod == 0){
		for (it=isUpdated.begin(); it != isUpdated.end(); it++) {
			it->second = false;
			mapofstatistic[it->first].clearResult();
			counter = 0;
		}
	} else {
		for (it=isUpdated.begin(); it != isUpdated.end(); it++) {
			if (it->second==true){
				tableOfNeighborPER[it->first] = mapofstatistic[it->first].getMean();
				it->second = false;
			}else{
				//if(((1-tableOfNeighborAssociability[it->first]) < 0) || ((1-tableOfNeighborAssociability[it->first]) > 1))
				//	opp_error("Error (1) in computing PER");
				//mapofstatistic[it->first].collect((1-tableOfNeighborAssociability[it->first])*perPenalty);
				//if (resetPeriod-counter <= 0)
				//	opp_error("BECCATO!!");
				//mapofstatistic[it->first].collect(perPenalty/(resetPeriod-counter));
				mapofstatistic[it->first].collect(perPenalty);
				if((mapofstatistic[it->first].getMean() <= 0) || (mapofstatistic[it->first].getMean() >= 1))
					opp_error("Error (2) in computing PER");
				tableOfNeighborPER[it->first] = mapofstatistic[it->first].getMean();
			}
		}

	}
	counter++;
	return;

}


void FG::updateGlobalStatistics() {

	int numPDvar   = 0;
	int numTESTvar = 0;
	MapOfInt::iterator it;
	MapOfInt::iterator ot;
	HowManyTest::iterator et;

	for (ot = howManyNewPD.begin(); ot != howManyNewPD.end(); ot++) {
		numPDvar += ot->second;
	}
	numPD = numPDvar;

	for (et = howmanytest.begin(); et != howmanytest.end(); et++) {
		for (it = (et->second).begin(); it != (et->second).end(); it++) {
			numTESTvar += it->second;
		}
	}
	numTEST = numTESTvar;

	controlPacketSent.record(numControlSent - numControlSentvar);
	numControlSentvar = numControlSent;
	controlPacketRec.record(numControlRec - numControlRecvar);
	numControlRecvar = numControlRec;

	return;
}


void FG::finish(){

	simtime_t t = simTime();
	if (t==0)
		return;

	EV << "RREQ Sent:             " << numRREQsent << endl;
	EV << "RREP Sent:             " << numRREPsent << endl;
	EV << "RTEST Sent:            " << numRTESTsent << endl;
	EV << "RTESTAck Sent:         " << numRTESTAcksent << endl;
	EV << "RERR Sent:             " << numRERRsent << endl;
	EV << "HELLO Sent:            " << numHELLOsent << endl;
	EV << "Total Control Sent:    " << numControlSent << endl;

	EV << "RREQ Rec:             " << numRREQrec << endl;
	EV << "RREP Rec:             " << numRREPrec << endl;
	EV << "RTEST Rec:            " << numRTESTrec << endl;
	EV << "RTESTAck Rec:         " << numRTESTAckrec << endl;
	EV << "RERR Rec:             " << numRERRrec << endl;
	EV << "HELLO Rec:            " << numHELLOrec << endl;
	EV << "Total Control Rec:    " << numControlRec << endl;

	EV << "Total PD:              " << numPD << endl;
	EV << "Total Error in PD:     " << numErrorInPD << endl;
	EV << "Total TEST:            " << numTEST << endl;

	recordScalar("simulated time", t);

	recordScalar("#RREQsent", numRREQsent);
	recordScalar("#RREPsent", numRREPsent);
	recordScalar("#RTESTsent", numRTESTsent);
	recordScalar("#RTESTAckSent", numRTESTAcksent);
	recordScalar("#RERRsent", numRERRsent);
	recordScalar("#HelloSent", numHELLOsent);
	recordScalar("#TotalControlSent", numControlSent);

	recordScalar("#RREQrec", numRREQrec);
	recordScalar("#RREPrec", numRREPrec);
	recordScalar("#RTESTrec", numRTESTrec);
	recordScalar("#RTESTAckRec", numRTESTAckrec);
	recordScalar("#RERRrec", numRERRrec);
	recordScalar("#HelloRec", numHELLOrec);
	recordScalar("#TotalControlRec", numControlRec);

	recordScalar("#numOfPD", numPD);
	recordScalar("#numOfErrorInPD", numErrorInPD);
	recordScalar("#numOfDrop", numDrop);
	recordScalar("#numOfTEST", numTEST);

}
